/* DTerminkalender.java
 *
 * Author:	Malcolm Staudacher
 * Date:	May 1, 2008
 *
 */

package ocumed.domaene.terminverwaltung;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.TreeSet;
import java.util.Vector;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DArzt;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DFehlzeit;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DOrdinationsZeit;
import ocumed.persistenz.dao.DAOFactory;
import ocumed.persistenz.dao.TerminDAO;

public class DTerminkalender {

	private DArzt m_arzt;
	private Vector<DTermin> m_termine;


	public DTerminkalender(DArzt arzt, Vector<DTermin> termine) {
		m_arzt = arzt;
		m_termine = termine;
	}


	/**
	 * Checks, if the specified times collide with an existing appointment.
	 * 
	 * @param anfang
	 *            The beginning of the time to check.
	 * @param ende
	 *            The end of the time to check.
	 * @return true, if the time period collides with an existing appointment,
	 *         false otherwise.
	 */
	public boolean checkCollision(Calendar anfang, Calendar ende) {
		TreeSet<DTermin> termine = getSortedAppointments();
		boolean before = false;
		boolean after = false;

		if (termine.size() > 0) {

			for (DTermin ct : termine) {

				// check if this existing appointment is outside the suggestion
				if (!ct.getTerminEnde().after(anfang)) {
					before = true;
				} else {
					before = false;
				}

				if (!ct.getTerminZeitpunkt().before(ende)) {
					after = true;
				} else {
					after = false;
				}

				if (!before) {
					if (!after) {
						return true;
					} else {
						return false;
					}
				}

			}

		}

		return false;
	}


	public DTerminkalender(Vector<DTermin> termine) {
		m_termine = termine;
	}


	public DArzt getArzt() {
		return m_arzt;
	}


	public void setArzt(DArzt arzt) {
		m_arzt = arzt;
	}


	public Vector<DTermin> getTermine() {		
		return m_termine;
	}


	public Vector<DTermin> getTermine(Calendar tag) {
		Vector<DTermin> termine = new Vector<DTermin>();

		for (DTermin t : m_termine) {

			if (tag.get(Calendar.YEAR) == t.getTerminZeitpunkt().get(
					Calendar.YEAR)
					&& tag.get(Calendar.MONTH) == t.getTerminZeitpunkt().get(
							Calendar.MONTH)
					&& tag.get(Calendar.DAY_OF_MONTH) == t.getTerminZeitpunkt()
							.get(Calendar.DAY_OF_MONTH)) {				
				termine.add(t);
			}

		}
		
		return termine;
	}


	public TreeSet<DTermin> getSortedAppointments() {
		TreeSet<DTermin> termine = new TreeSet<DTermin>();

		for (DTermin termin : m_termine) {
			termine.add(termin);
		}

		return termine;
	}


	public void terminHinzufuegen(DTermin termin) {
		m_termine.add(termin);
	}
	
	
	public ArrayList<Date> getTerminVorschlaege(Integer anzahl, int dauer) {
		ArrayList<Date> vorschlaege = new ArrayList<Date>(anzahl);
		Calendar anfang = Calendar.getInstance();
		Calendar ende = Calendar.getInstance();
		Calendar limit = Calendar.getInstance();
		int gefunden = 0;

		// disregard seconds and milliseconds
		anfang.set(Calendar.SECOND, 0);
		anfang.set(Calendar.MILLISECOND, 0);

		// set limit for search
		limit.add(Calendar.MONTH, 18);
		
		// get 'anzahl' suggestions
		while (gefunden < anzahl && anfang.before(limit)) {

			// go to the next day
			anfang.add(Calendar.DAY_OF_MONTH, 1);

			// get sorted working times for the day
			TreeSet<DOrdinationsZeit> sortOrdZeiten = m_arzt.sortOrdZeiten(anfang);

			// check all working times of the day, if necessary
			for (DOrdinationsZeit oz : sortOrdZeiten) {

				// set first suggestion 10 minutes before the beginning of the
				// working time
				anfang.set(Calendar.HOUR_OF_DAY, oz.getAnfang().get(
						Calendar.HOUR_OF_DAY));
				anfang
						.set(Calendar.MINUTE, oz.getAnfang().get(
								Calendar.MINUTE));
				anfang.add(Calendar.MINUTE, -10);

				// shift appointment by 10 minutes until end of the working time
				// is reached, or by 'dauer' minutes if an appointment was found
				while (!ende.after(oz.getEnde())) {
					anfang.add(Calendar.MINUTE, 10);
					ende = (Calendar) anfang.clone();
					ende.add(Calendar.MINUTE, dauer);

					// check if it's within an absence
					boolean invalid = false;

					for (DFehlzeit fz : m_arzt.getFehlzeiten()) {
						// if the current suggestion is between these borders
						if (anfang.compareTo(fz.getVon()) >= 0
								&& ende.compareTo(fz.getBis()) <= 0) {
							invalid = true;
						}

					}

					// force next
					if (invalid) {
						continue;
					}

					// check if there is a collision with an appointment
					boolean collision = checkCollision(anfang, ende);

					// check if it's still within the current days working time
					if ((!collision) && anfang.compareTo(oz.getAnfang()) >= 0
							&& ende.compareTo(oz.getEnde()) <= 0) {
						vorschlaege.add(anfang.getTime());

						// go to next day
						anfang.set(Calendar.HOUR_OF_DAY, oz.getEnde().get(
								Calendar.HOUR_OF_DAY) + 1);
						ende = (Calendar) anfang.clone();
						gefunden++;
					}

				} // end check if end of working time is reached

			} // end all working times check
		} // end suggestion count
		
		return vorschlaege;
	}

}
